package vcs

import (
	"errors"
	"fmt"
	git "github.com/go-git/go-git/v6"
	"github.com/go-git/go-git/v6/plumbing"
	"github.com/go-git/go-git/v6/plumbing/transport/http"
	"mon-go/src/utils"
	"os"
	"path"
	"path/filepath"
	"strings"
)

type GitConfig struct {
	UserName string
	Password string
	Path     string
}

type GitProject struct {
	GitConfig GitConfig
	RepoUrl   string
	Branch    string
	Module    string
}

func GetGitProjectPath(gitProject *GitProject) string {
	join := filepath.Join(gitProject.GitConfig.Path, gitProject.Module)
	return join
}

func GetGitProjectRepoPath(gitProject *GitProject) string {
	join := filepath.Join(gitProject.GitConfig.Path, gitProject.Module, ".git")
	return join
}

func ExistGitProject(gitProject *GitProject) bool {
	repoPath := GetGitProjectRepoPath(gitProject)
	_, err := os.Stat(repoPath)
	return err == nil || !os.IsNotExist(err)
}

func CheckIfError(err error) {
	if err == nil {
		return
	}

	fmt.Printf("\x1b[31;1m%s\x1b[0m\n", fmt.Sprintf("error: %s", err))
	os.Exit(1)
}

func Info(format string, args ...interface{}) {
	fmt.Printf("\x1b[34;1m%s\x1b[0m\n", fmt.Sprintf(format, args...))
}

func Warning(format string, args ...interface{}) {
	fmt.Printf("\x1b[36;1m%s\x1b[0m\n", fmt.Sprintf(format, args...))
}

func GetModuleName(repoUrl string) string {
	base := path.Base(repoUrl)
	name := strings.TrimSuffix(base, ".git")
	return name
}

func (gitProject *GitProject) Init() {
	if gitProject.Module == "" {
		gitProject.Module = GetModuleName(gitProject.RepoUrl)
	}
}

func (gitProject *GitProject) FetchCode() {
	if ExistGitProject(gitProject) {
		err := gitProject.FetchRemote()
		print(err)
	} else {
		gitProject.Clone()
	}
}

func (gitProject *GitProject) Clone() {
	gitProjectPath := GetGitProjectPath(gitProject)
	utils.CreateDirIfNotExist(gitProjectPath)
	r, err := git.PlainClone(gitProjectPath, &git.CloneOptions{
		Auth: &http.BasicAuth{
			Username: gitProject.GitConfig.UserName,
			Password: gitProject.GitConfig.Password,
		},
		ReferenceName: plumbing.NewBranchReferenceName(gitProject.Branch),
		URL:           gitProject.RepoUrl,
		Progress:      os.Stdout,
	})
	CheckIfError(err)

	ref, err := r.Head()
	CheckIfError(err)

	commit, err := r.CommitObject(ref.Hash())
	CheckIfError(err)

	fmt.Println(commit)
}

func (gitProject *GitProject) FetchRemote() error {
	repoPath := GetGitProjectPath(gitProject)
	// 打开本地仓库
	repo, err := git.PlainOpen(repoPath)
	if err != nil {
		fmt.Printf("打开仓库失败: %v\n", err)
		return err
	}

	// 获取工作树
	worktree, err := repo.Worktree()
	if err != nil {
		fmt.Printf("获取工作树失败: %v\n", err)
		return err
	}

	remoteRef, err := repo.Reference(plumbing.NewRemoteReferenceName("origin", gitProject.Branch), true)
	if err != nil {
		fmt.Printf("获取远程分支失败: %v\n", err)
		return err
	}

	// 执行 fetch 操作以更新远程信息
	err = repo.Fetch(&git.FetchOptions{
		RemoteName: "origin",
		Auth: &http.BasicAuth{
			Username: gitProject.GitConfig.UserName,
			Password: gitProject.GitConfig.Password,
		},
	})

	if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
		fmt.Printf("fetch 失败: %v\n", err)
		return err
	}

	// 重置工作树到远程分支的状态（放弃本地修改）
	err = worktree.Reset(&git.ResetOptions{
		Mode:   git.HardReset, // 使用 HardReset 放弃所有本地修改
		Commit: remoteRef.Hash(),
	})
	if err != nil {
		fmt.Printf("重置失败: %v\n", err)
		return err
	}

	fmt.Println("成功重置到远程分支，放弃本地修改")
	return nil
}
